package cn.funeralobjects.youtube.dl.extension.exec;

import cn.funeralobjects.youtube.dl.extension.exception.YoutubeDlException;
import cn.funeralobjects.youtube.dl.extension.model.Running;
import cn.funeralobjects.youtube.dl.extension.model.WatchInfo;
import cn.funeralobjects.youtube.dl.extension.utils.RunningLogUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.experimental.Accessors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * @author FuneralObjects
 * Create date: 2020/4/3 2:23 PM
 */
@Data
@Accessors(chain = true)
public class YoutubeDl {
    private static final Logger LOGGER = LoggerFactory.getLogger(YoutubeDl.class);
    private static final String BASE_URL = "https://www.youtube.com/watch?v=";
    private static final ObjectMapper MAPPER = new ObjectMapper();
    public static WatchInfo getInfo(String id){
        return process(watchProcess(BASE_URL+id,null));
    }

    public static WatchInfo getInfo(String id, String proxy){
        return process(watchProcess(BASE_URL+id,proxy));
    }

    public static Running download(String id, String format){
        return new Running(downloadProcess(format, BASE_URL + id, null, null), RunningLogUtils.getWriter(id, format));
    }

    public static Running downloadInSpecialFolder(String id, String format, String folder){
        return new Running(downloadProcess(format, BASE_URL + id, folder + "/" + id + "/" + format, null), RunningLogUtils.getWriter(id, format));
    }

    public static Running downloadInSpecialFolder(String id, String format, String proxy, String folder){
        return new Running(downloadProcess(format, BASE_URL + id, folder + "/" + id + "/" + format, proxy), RunningLogUtils.getWriter(id, format));
    }

    private static WatchInfo process(Process process){
        try {
            BufferedReader resultReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String temp;
            StringBuilder result = new StringBuilder();
            StringBuilder error = new StringBuilder();
            while ( (temp = resultReader.readLine()) != null ){
                if(temp.startsWith("ERROR")){
                    error.append(temp);
                }else{
                    result.append(temp);
                }
            }
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            while ( (temp = errorReader.readLine())!= null ){
                error.append(temp);
            }
            if(error.length() > 0){
                throw new YoutubeDlException(error.toString());
            }
            if(result.length() == 0){
                return null;
            }
            return MAPPER.readValue(result.toString(), WatchInfo.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static Process downloadProcess(String format, String url, String folder, String proxy){
        YoutubeDlExec exec = new YoutubeDlExec();
        if (isEmpty(format) || isEmpty(url)) {
            throw new IllegalArgumentException("The args of format or url or folder are invalid!");
        }
        if (!isEmpty(proxy)) {
            exec.useProxy(proxy);
        }
        if (!isEmpty(folder)) {
            exec.useFolder(folder);
        }
        return exec
                .timeout(10)
                .enableDownloadThumbnail()
                .download(format, url);
    }


    private static Process watchProcess(String url, String proxy){
        YoutubeDlExec exec = new YoutubeDlExec();
        if(isEmpty(url)){
            throw new IllegalArgumentException("The args for url cannot is invalid!");
        }
        if(!isEmpty(proxy)){
            exec.useProxy(proxy);
        }
        return exec.enableDownloadThumbnail().watch(url);
    }

    private static boolean isEmpty(String str){
        return org.springframework.util.StringUtils.isEmpty(str);
    }

    private static class YoutubeDlExec{
        private List<String> args;

        YoutubeDlExec(){
            this.args = new ArrayList<>();
            this.args.add("youtube-dl");
        }

        public YoutubeDlExec useProxy(String proxy){
            this.args.add("--proxy");
            this.args.add(proxy);
            return this;
        }

        public YoutubeDlExec enableDownloadThumbnail() {
            this.args.add("--write-thumbnail");
            return this;
        }

        public YoutubeDlExec useFolder(String folder) {
            this.args.add("-o");
            this.args.add(folder + "/%(title)s.%(ext)s");
            return this;
        }

        public YoutubeDlExec timeout(Integer second) {
            this.args.add("--socket-timeout");
            this.args.add(String.valueOf(second));
            return this;
        }

        public Process download(String format, String url) {
            this.args.add("-f");
            this.args.add(format);
            this.args.add(url);
            return this.exec();
        }

        public Process watch(String url) {
            this.args.add("-j");
            this.args.add(url);
            return this.exec();
        }

        private Process exec(){
            try {
                String[] bash = this.args.toArray(new String[0]);
                LOGGER.info("Youtube-DL Exec:{}", String.join(" ", bash));
                return Runtime.getRuntime().exec(this.args.toArray(new String[0]));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
